﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpMirrorActionBehavior.h"
#include "IActionInputEventArgs.h"
#include "IUiView.h"
#include "IModelView.h"
#include "MirrorUtils.h"
#include "IUiDocument.h"
#include "IUserTransaction.h"
#include "UiCommonDialog.h"
#include "ISelection.h"
#include "GbmpModelViewTypes.h"
#include "MathUtils.h"
#include "INotificationManager.h"
#include "INotificationDetailItem.h"
#include "INotification.h"
#include "IRadioButtonGroupDefinition.h"
#include "ICheckBoxGroupDefinition.h"
#include "IControlDefinition.h"
#include "IPickFilter.h"
#include "IPickTarget.h"
#include "IGraphicsCurve3d.h"
#include "IGraphicsBRepFace.h"
#include "IGraphicsBRepEdge.h"
#include "GUiBaseInterfaceEnums.h"


#include "IUiDocumentViewManager.h"
#include "IPlane.h"
#include "IHighlights.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;


#define ACTION_RADIO_AXIS_TYPE          L"gmRadioAxisType"
#define ACTION_CHECK_COPY               L"gmCheckCopy"

namespace
{
    std::wstring s_Pick_Axis = GBMP_TR(L"拾取轴");
    std::wstring s_Draw_Axis = GBMP_TR(L"绘制轴");
    std::wstring s_Mirror_By_Copy = GBMP_TR(L"复制");

    class GbmpPickFilter : public IPickFilter
    {
    public:
        GbmpPickFilter() {};
        GbmpPickFilter(const IDocument* pDocument)
            :m_pDocument(pDocument) {};
        virtual ~GbmpPickFilter() {};

        // 是否允许拾取此Element GRep及其子节点
        virtual bool AllowElement(const ElementId& elemId) const override { return true; }

        // 是否允许拾取nodeReference对应的节点
        virtual bool AllowGraphicsNode(const IGraphicsNodeReference& nodeReference) const override{ return true; }

        //设置PickTarget中选项的接口，PickNodeReferenceAction利用其进行PickTarget的设置
        virtual bool SetPickTargetOption(IPickTarget* pickTarget) override
        {
            pickTarget->EnableGraphicsElementShape();
            return true;
        }

    protected:
        const IDocument* GetDocument() const { return m_pDocument; }

    private:
        const IDocument *m_pDocument;

    };

    class GbmpMirrorPickFilter : public gcmp::IPickFilter
    {
    public:
        GbmpMirrorPickFilter(gcmp::IDocument* pDocument);
        ~GbmpMirrorPickFilter(void);

        virtual bool AllowElement(const gcmp::ElementId& id) const override;
        virtual bool AllowGraphicsNode(const gcmp::IGraphicsNodeReference& nodeReference) const override;
        void SetAllowAllElement(bool bEnable);
        bool GetAllowAllElement()const { return m_bAllowAllElement; };

        virtual bool SetPickTargetOption(gcmp::IPickTarget* pickTarget) override;
        void SetWorkPlaneNormal(const gcmp::Vector3d& workPlaneNormal);
        void SetExcludeNodeReferences(const gcmp::GraphicsNodeReferenceOwnerPtrVector& pickedNodeReferences);
        void SetSketchMode(const bool& mode) { m_bSketchMode = mode; }
        bool GetSketchMode()const { return m_bSketchMode; }


    private:

        gcmp::Vector3d m_workPlaneNormal;  //工作平面的法线
        gcmp::GraphicsNodeReferenceOwnerPtrVector m_excludeNodeReferences; //过滤之前已经拾取到的对象，避免重复拾取
        bool m_bAllowAllElement;
        bool m_bSketchMode;
    };


    GbmpMirrorPickFilter::GbmpMirrorPickFilter(gcmp::IDocument* pDocument)
        :m_bAllowAllElement(false),
        m_bSketchMode(false)
    {
    }

    GbmpMirrorPickFilter::~GbmpMirrorPickFilter(void)
    {
    }
    bool GbmpMirrorPickFilter::AllowElement(const gcmp::ElementId& elementID) const
    {
        if (m_bAllowAllElement)
        {
            return true;
        }

        return false;
    }
    bool GbmpMirrorPickFilter::AllowGraphicsNode(const IGraphicsNodeReference& nodeReference) const
    {
        if (GetSketchMode())
        {
            FOR_EACH(excludeNode, m_excludeNodeReferences)
            {
                if (excludeNode->GetElementId() == nodeReference.GetElementId())
                    return false;
            }
        }

        const IGraphicsNode* pNode = nodeReference.GetUntransformedIntersectGraphicsNode();
        DBG_WARN_AND_RETURN_UNLESS(nullptr != pNode, false, L"pNode为空",L"GDMPLab",L"2024-03-30");

        const IGraphicsCurve3d* pGCurve = dynamic_cast<const IGraphicsCurve3d*>(pNode);
        if (pGCurve && pGCurve->GetType() == GraphicsNodeType::Line3d)
        {
            return true;
        }
        const IGraphicsBRepFace* pGFace = dynamic_cast<const IGraphicsBRepFace*>(pNode);
        if (pGFace)
        {
            if (pGFace && pGFace->IsPlanar())
            {
                Vector2d vector2d(0, 0);
                Vector3d normal = pGFace->GetNormal(vector2d);
                double dt = m_workPlaneNormal * normal;
                if (fabs(dt) < Constants::DOUBLE_EPS)
                {
                    return true;
                }
            }

        }

        const IGraphicsBRepEdge* pGEdge = dynamic_cast<const IGraphicsBRepEdge*>(pNode);
        if (pGEdge)
        {
            if (pGEdge && pGEdge->IsStraightLine())
            {
                return true;
            }
        }

        return false;
    }
    void GbmpMirrorPickFilter::SetAllowAllElement(bool bEnable)
    {
        this->m_bAllowAllElement = bEnable;
    }

    bool GbmpMirrorPickFilter::SetPickTargetOption(IPickTarget* pickTarget)
    {
        pickTarget->DisableAll();
        pickTarget->EnableCurve();
        pickTarget->EnableFace();
        pickTarget->EnableSegmentOfPolyCurve();
        return true;
    }
    void GbmpMirrorPickFilter::SetWorkPlaneNormal(const Vector3d& workPlaneNormal)
    {
        m_workPlaneNormal = workPlaneNormal;
    }

    void GbmpMirrorPickFilter::SetExcludeNodeReferences(const GraphicsNodeReferenceOwnerPtrVector& pickedNodeReferences)
    {
        OwnerPtrContainerUtil::CopyItems(m_excludeNodeReferences, pickedNodeReferences);
    }
}

gcmp::GbmpMirrorActionBehavior::GbmpMirrorActionBehavior() : m_bPickAxis(true), m_bCopy(true)
{
}

bool gcmp::GbmpMirrorActionBehavior::DefineInputDialog(std::wstring &title, std::vector<OwnerPtr<IControlDefinition>>& inputItems)
{
    title = GBMP_TR(L"镜像");

    // 轴
    OwnerPtr<IRadioButtonGroupDefinition> opAxisTypeGroupDef = IRadioButtonGroupDefinition::Create(ACTION_RADIO_AXIS_TYPE, GBMP_TR(L"轴类型"));
    opAxisTypeGroupDef->AddRadioButton(s_Pick_Axis, s_Pick_Axis, s_Pick_Axis);
    opAxisTypeGroupDef->AddRadioButton(s_Draw_Axis, s_Draw_Axis, s_Draw_Axis);
    opAxisTypeGroupDef->SetCurrentRadioButton(s_Pick_Axis);
    inputItems.push_back(TransferOwnership(opAxisTypeGroupDef));

    // 复制
    OwnerPtr<ICheckBoxGroupDefinition> pCheckBoxGroupDef = ICheckBoxGroupDefinition::Create(ACTION_CHECK_COPY, ACTION_CHECK_COPY);
    CheckState checkState = m_bCopy ? CheckState::Checked : CheckState::Unchecked;
    pCheckBoxGroupDef->AddCheckBox(s_Mirror_By_Copy, GBMP_TR(s_Mirror_By_Copy), GBMP_TR(s_Mirror_By_Copy), checkState);
    inputItems.push_back(TransferOwnership(pCheckBoxGroupDef));

    return true;
}

void gcmp::GbmpMirrorActionBehavior::OnInputEvent(IActionInputEventArgs* pArgs)
{
    const std::pair<std::wstring, gcmp::Any>& msg = pArgs->GetInputMessage();
    const std::wstring eventID = msg.first;
    gcmp::Any inputValue = msg.second;
    if (eventID == ACTION_RADIO_AXIS_TYPE)
    {
        m_bPickAxis = inputValue.cast<std::wstring>() == s_Pick_Axis ? true : false;
    }
    else if (eventID == std::wstring(ACTION_CHECK_COPY) + L"_" + s_Mirror_By_Copy)
    {
        m_bCopy = inputValue.cast<bool>();
    }
}

bool gcmp::GbmpMirrorActionBehavior::ValidateMirrorAxis(IUiView* pCurrentView, const Matrix4d& mirrorMatrix, const GraphicsNodeReferenceOwnerPtrVector& nodeToBeMirrored)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(nullptr != pCurrentView, L"pCurrentView为空",L"GDMPLab",L"2024-03-30");
    IModelView *pModelView = pCurrentView->GetModelView();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(nullptr != pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    std::wstring viewType = pModelView->GetUserDataId();

    DBG_WARN_AND_RETURN_FALSE_UNLESS(viewType != L"", L"pModelView为空",L"GDMPLab",L"2024-03-30");
    /// \ gcmp镜像定制行为，根据mirrorMatri立面视图的特点:镜像后z坐标不变来判断镜像轴是否合格
    if (viewType == ElevationViewType || viewType == SectionViewType)
    {
        if (!MathUtils::IsEqual(mirrorMatrix[2][2], 1, Constants::DOUBLE_EPS))
        {
            std::vector<OwnerPtr<INotificationDetailItem>> detailItems;
            INotificationManager::Get()->AddNotification(TransferOwnership(INotification::CreateNotification(NotificationTypes::InfomationNotificationType, GBMP_TR(L"镜像轴必须竖直"), std::move(detailItems))));
            return false;
        }
    }
    return true;
}

void gcmp::GbmpMirrorActionBehavior::OnMirrorFinished(IUiView* pCurrentView, const Matrix4d& mirrorMatrix, const GraphicsNodeReferenceOwnerPtrVector& nodeToBeMirrored)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(nullptr != pCurrentView, L"pCurrentView为空",L"GDMPLab",L"2024-03-30");
    IDocument* pDoc = pCurrentView->GetUiDocument()->GetDbDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc != nullptr, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    std::set<ElementId> elements2BeMirroredSet;
    for (const auto& opNodeRef : nodeToBeMirrored)
        elements2BeMirroredSet.insert(opNodeRef->GetElementId());
    if (elements2BeMirroredSet.empty())
        return;   

    std::vector<ElementId> elementsMirrored;
    std::wstring strError;
    OwnerPtr<IUserTransaction> ut = IUserTransaction::Create(pDoc, GBMP_TR(L"镜像"));
    DBG_WARN_AND_RETURN_VOID_UNLESS(ut != nullptr, L"事务创建失败",L"GDMPLab",L"2024-03-30");
    if (!MirrorUtils::MirrorElements(pDoc, std::vector<ElementId>(elements2BeMirroredSet.begin(), elements2BeMirroredSet.end()), mirrorMatrix, m_bCopy, elementsMirrored, &strError))
    {
        UiCommonDialog::ShowMessageBox(GBMP_TR(L"镜像"), strError, (int)UiCommonDialog::ButtonType::OK);
        return;
    }

    ut->Commit();

    ISelection::Get()->Clear(pDoc);
    IHighlights::Get()->Clear();

    GraphicsNodeReferenceOwnerPtrSet newSelections;
    std::vector<OwnerPtr<IGraphicsNodeReference>> newHighlights;
    FOR_EACH(modelId, elementsMirrored)
    {
        OwnerPtr<IGraphicsNodeReference> opGnodeRef = IGraphicsNodeReference::CreateGraphicsElementShapeReference(modelId);
        newSelections.insert(opGnodeRef->Clone());
        newHighlights.push_back(TransferOwnership(opGnodeRef));
    }

    // 集中设置，减少由选择集、高亮集变更引起的性能问题
    ISelection::Get()->AddGroupGraphicsNodeReference(pDoc, newSelections);
    IHighlights::Get()->AddGraphicsNodeReferences(newHighlights);
}

bool gcmp::GbmpMirrorActionBehavior::IsPickAxis() const
{
    return m_bPickAxis;
}

bool gcmp::GbmpMirrorActionBehavior::IsCopyElemments() const
{
    return m_bCopy;
}

OwnerPtr<IPickFilter> GbmpMirrorActionBehavior::GetMirrorObjectPickFilter(IDocument* pDocument)
{
    return NEW_AS_OWNER_PTR(GbmpPickFilter, pDocument);
}

OwnerPtr<IPickFilter> GbmpMirrorActionBehavior::GetMirrorAxisPickFilter(IDocument* pDocument, const gcmp::GraphicsNodeReferenceOwnerPtrVector& excludeNodeReferences)
{
    DBG_WARN_UNLESS(pDocument != nullptr, L"pDocument为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<GbmpMirrorPickFilter> opPickFilter = NEW_AS_OWNER_PTR(GbmpMirrorPickFilter, pDocument);

    DBG_WARN_UNLESS(IUiDocumentViewManager::Get() != nullptr, L"IUiDocumentViewManager::Get()为空",L"GDMPLab",L"2024-03-30");
    IUiView* pCurrentView = IUiDocumentViewManager::Get()->GetCurrentUiView();
    DBG_WARN_UNLESS(pCurrentView != nullptr, L"pCurrentView为空",L"GDMPLab",L"2024-03-30");
    IModelView* pModelView = pCurrentView->GetModelView();
    DBG_WARN_UNLESS(pModelView != nullptr, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    Vector3d workPlaneNormal = pModelView->GetWorkPlane()->GetNormal();

    opPickFilter->SetWorkPlaneNormal(workPlaneNormal);
    opPickFilter->SetAllowAllElement(true);
    opPickFilter->SetExcludeNodeReferences(excludeNodeReferences);

    return TransferOwnership(opPickFilter);
}
